package com.csnj.admin.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.csnj.common.annotation.ApiNeedLog;
import com.csnj.common.annotation.LoginSessioinCheck;
import com.csnj.common.annotation.ModelName;
import com.csnj.common.annotation.Resubmit;
import com.csnj.common.constant.CommonConstants;
import com.csnj.common.utils.BaseContextHandler;
import com.csnj.common.utils.EntityUtils;
import com.csnj.common.utils.IpUtil;
import com.csnj.common.utils.ResubmitLock;
import com.csnj.core.Srvs.SysManager.SysLogSrvs;
import com.csnj.core.entity.SysLog;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by ace on 2017/9/10.
 */
@Log4j2
public class UserAccessLogInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    SysLogSrvs sysLogSrvs;
    private final static Object PRESENT = new Object();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean validRequest = handler instanceof HandlerMethod;
        if(!validRequest) {
            return super.preHandle(request, response, handler);
        }
        String uid = (String) request.getSession().getAttribute(CommonConstants.CONTEXT_KEY_USER_ID);
        String name = (String) request.getSession().getAttribute(CommonConstants.CONTEXT_KEY_USER_NAME);
        Long startTime = System.currentTimeMillis();
        BaseContextHandler.set(CommonConstants.CONTEXT_KEY_TIME, startTime);
        BaseContextHandler.setUserID(uid);
        BaseContextHandler.setUsername(name);

        if(request.getRequestURL().indexOf("swagger")>0 ||
                request.getRequestURL().indexOf("webjar")>0) {
            return super.preHandle(request, response, handler);
        }

        if(request.getRequestURI().indexOf("/error")==0) {
            return super.preHandle(request, response, handler);
        }

        validRequest = inWhiteList(request.getRequestURI()) || (uid!=null && !uid.isEmpty());
        if(!validRequest) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        LoginSessioinCheck sessionCheckAnnotation = handlerMethod.getMethodAnnotation(LoginSessioinCheck.class);
        if(sessionCheckAnnotation!=null) {
            if(uid == null || "0".equals(uid) || uid.isEmpty()) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return false;
            }
        }

        RequestWrapper requestWrapper = new RequestWrapper(request);
        String body = requestWrapper.getBody();

        //重复提交校验
        if("post".equalsIgnoreCase(request.getMethod()) || "put".equalsIgnoreCase(request.getMethod())) {
            //获取注解信息
            Resubmit annotation = handlerMethod.getMethodAnnotation(Resubmit.class);
            if(annotation!=null) {
                int delaySeconds = annotation.delaySeconds();
                //获取请求参数
                JSONObject parameterMap = JSON.parseObject(body);
                String key = request.getRequestURI();
                if(parameterMap !=  null) {
                    key = key + ResubmitLock.handleKey(parameterMap.toJSONString());
                }
                //执行锁
                boolean lock = false;
                try {
                    //设置解锁key
                    lock = ResubmitLock.getInstance().lock(key, PRESENT);
                    if (!lock) {
                        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                        return false;
                    }
                } finally {
                    //设置解锁key和解锁时间
                    ResubmitLock.getInstance().unLock(lock, key, delaySeconds);
                }
            }
        }

        // 配置该注解，说明不进行用户拦截
        ApiNeedLog annotation = handlerMethod.getMethodAnnotation(ApiNeedLog.class);
        if (annotation == null) {
            return super.preHandle(requestWrapper, response, handler);
        }
        ModelName moduleName = handlerMethod.getBeanType().getAnnotation(ModelName.class);
        MethodParameter[] m = handlerMethod.getMethodParameters();
        if(moduleName == null) {
            return super.preHandle(requestWrapper, response, handler);
        }
        if("post".equalsIgnoreCase(request.getMethod()) || "put".equalsIgnoreCase(request.getMethod())) {
            BaseContextHandler.set(CommonConstants.CONTEXT_KEY_PARAM, body.substring(0, body.length()>10240? 10240: body.length()));
        }
        return super.preHandle(requestWrapper, response, handler);
    }

    private boolean inWhiteList(String requestURL) {
        String[] uri = {"/auth/token"};
        for(String i: uri) {
            if(requestURL.indexOf(i)>=0) {
                return true;
            };
        }
        return false;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        boolean validRequest = handler instanceof HandlerMethod;
        if(!validRequest) {
            return;
        }

        if(request.getRequestURL().indexOf("swagger")>0 ||
                request.getRequestURL().indexOf("webjar")>0) {
            super.afterCompletion(request, response, handler, ex);
            return;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 配置该注解，说明不进行用户拦截
        MethodParameter[] m = handlerMethod.getMethodParameters();
        ApiNeedLog annotation = handlerMethod.getMethodAnnotation(ApiNeedLog.class);

        if (annotation == null) {
            super.afterCompletion(request, response, handler, ex);
            return;
        }

        ModelName moduleName = handlerMethod.getBeanType().getAnnotation(ModelName.class);
        if(moduleName == null) {
            super.afterCompletion(request, response, handler, ex);
            return;
        }

        try {
            HttpServletRequest req = (HttpServletRequest) request;
            Long endTime = System.currentTimeMillis();
            Long startTime = (Long) BaseContextHandler.get(CommonConstants.CONTEXT_KEY_TIME);
            String param;
            if("post".equalsIgnoreCase(request.getMethod()) || "put".equalsIgnoreCase(request.getMethod())) {
                param = (String) BaseContextHandler.get(CommonConstants.CONTEXT_KEY_PARAM);
            } else {
                Map<String, String[]> tm = req.getParameterMap();
                HashMap<String, String[]> t = new HashMap<>(16);
                for (String o : tm.keySet()) {
                    if (!"token".equals(o)) {
                        t.put(o, tm.get(o));
                    }
                }
                param = JSON.toJSONString(t);
            }
            log.info("URI:" + req.getRequestURI());
            log.info("Method:" + req.getMethod());
            log.info("Param:" + param);
            log.info("Status:" + String.valueOf(response.getStatus()));
            log.info("RequestTime:" + new Date().toString());
            log.info("Times:" + String.valueOf(endTime - startTime) + "ms");
            log.info("ResponseSize:"+response.getBufferSize());

            SysLog log = new SysLog();
            String uid = BaseContextHandler.getUserID();
            String uname = BaseContextHandler.getUsername();
            if(uid==null || uid.isEmpty()) {
                log.setUserId(0);
                log.setUserName("未知");
            } else {
                log.setUserId(Integer.valueOf(uid));
                log.setUserName(uname);
            }
            log.setLogDesc(annotation.desc());
            log.setModule(moduleName.ModelName());
            log.setRequestUrl(req.getRequestURI());
            log.setRequestParam(param);
            log.setSystime((int) (endTime - startTime));
            log.setStatus(response.getStatus());
            log.setUserIp(IpUtil.getIp(request));
            EntityUtils.setCreatAndUpdatInfo(log);
            sysLogSrvs.insertSelectiveAsync(log);
        } catch (Exception e) {
            log.error("===Save access log Error===");
            e.printStackTrace();
        }

        BaseContextHandler.remove();
        super.afterCompletion(request, response, handler, ex);
    }
}
